package xj.graph2d.shapes;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.codec.binary.Base64;

import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.RectangleShape;
import xj.lang.CodeWriter;
import xj.util.xml.XMLUtil;

public class ImageShape 
  extends RectangleShape 
  implements ShapeConstants {

  public ImageShape() {
  }

  public ImageShape(String filename) throws IOException {
    this(filename, false);
  }

  public ImageShape(File f) throws IOException {
    this(f, false);
  }

  public ImageShape(BufferedImage image) {
    this(image, false);
  }

  public ImageShape(String filename, boolean createComp) throws IOException {
    super(false, createComp);
    showborder = false;
    if (filename != null) {
      File f = new File(filename);
      this.filename = filename;
      setImage(ImageIO.read(f));
    }
  }

  public ImageShape(File f, boolean createComp) throws IOException {
    super(false, createComp);
    showborder = false;
    if (f != null) {
      this.filename = f.getName();
      setImage(ImageIO.read(f));
    }
  }

  public ImageShape(BufferedImage image, boolean createComp) {
    super(false, createComp);
    showborder = false;
    setImage(image);
  }

  protected ImageShape(String filename, BufferedImage image, boolean createComp) {
    super(false, createComp);
    showborder = false;
    this.filename = filename;
    setImage(image);
  }

  @Override
  public DrawShape makeInstance() {
    return new ImageShape((BufferedImage) null, comp != null);
  }

  @Override
public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    super.copyTo(s);
    if (s instanceof ImageShape) { 
      ImageShape ishape = (ImageShape) s;
      ishape.setImage(this.image);
    }
  }

  @Override
  public String getName() {
    return "Image";
  }

  public Image getImage() {
    return image;
  }

  public void setImage(BufferedImage image) {
    if (image != null) {
      this.image = image;
      imageWidth = image.getWidth(null);
      imageHeight = image.getHeight(null);
      float x1 = (float) p1.getX();
      float y1 = (float) p1.getY();
      setEnds(x1, y1, x1 + imageWidth, y1 + imageHeight);
    }
  }

  public void setImage(File f) throws IOException {
    if (f != null) {
      this.filename = f.getName();
      setImage(ImageIO.read(f));
    }
  }

  public void setImage(String filename) throws IOException {
    if (filename != null) {
      File f = new File(filename);
      this.filename = filename;
      setImage(ImageIO.read(f));
    }
  }

  public String getFilename() {
    return filename;
  }

  @Override
  public void draw(Graphics2D g2) {
    float x = (float) Math.min(p1.getX(), p2.getX());
    float y = (float) Math.min(p1.getY(), p2.getY());
    float w = (float) Math.abs(p1.getX() - p2.getX());
    float h = (float) Math.abs(p1.getY() - p2.getY());
    if ((int) w >= 1 && (int) h >= 1) {
      g2.drawImage(scaledImage, (int) x, (int) y, null);
    }
    if (showborder) {
      g2.draw(new Rectangle2D.Float(x, y, w, h));
    }
  }

  @Override
  public void calculateBounds() {
    super.calculateBounds();
    int w = (int) Math.abs(p1.getX() - p2.getX());
    int h = (int) Math.abs(p1.getY() - p2.getY());
    if (w >= 1 && h >= 1 && 
	(w != imageWidth || h != imageHeight) && 
	image != null) {
      scaledImage = image.getScaledInstance(w, h, Image.SCALE_SMOOTH); 
      //Image.SCALE_FAST); 
    } else {
      scaledImage = image;
    }
  }

  @Override
  protected void writeXMLElements(CodeWriter out, int mode,
				  String namespacePrefix) {
    if (out != null) {
      super.writeXMLElements(out, mode, namespacePrefix);

      String rname = getPrefixedName("Image", namespacePrefix);
      out.print("<" + rname + ">");
      try {
	ByteArrayOutputStream bout = new ByteArrayOutputStream(imageHeight * imageWidth);
	ImageIO.write(image, "png", bout);
	byte[] ba = Base64.encodeBase64(bout.toByteArray());
	String str = new String(ba, "US-ASCII");
	out.print(str);
      } catch (IOException e) {
	System.err.println(e.getMessage());
      }
      out.println("</" + rname + ">");

    }
  }

  @Override
  protected void processXMLElement(org.w3c.dom.Element e, String namespaceURI) {
    if (e != null) {
      String nodename = e.getLocalName();
      if ("Image".equals(nodename)) {
	try {
	  String val = XMLUtil.getTextChildNodeValue(e);
	  if (val != null) {
	    val = val.trim();
	  }
	  byte[] ba = val.getBytes("US-ASCII");
	  byte[] ba2 = Base64.decodeBase64(ba);
	  ByteArrayInputStream bin = new ByteArrayInputStream(ba2);
	  BufferedImage bimage = ImageIO.read(bin);
	  this.image = bimage;
	  imageWidth = image.getWidth(null);
	  imageHeight = image.getHeight(null);
	  calculateBounds();
	  //setImage(bimage);
	} catch (IOException ex) {
	  System.err.println(ex.getMessage());
	}
      } else {
	super.processXMLElement(e, namespaceURI);
      }
    }
  }

  @Override
  public boolean isZeroSize() {
    return super.isZeroSize() || (image == null);
  }

  protected BufferedImage image;

  protected Image scaledImage;

  protected int imageHeight, imageWidth;

  protected String filename;

}
